package com.yungu.swift.common.sensitiveword;

import com.yungu.swift.autoconfig.redis.RedisCacheService;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 初始化敏感词库，将敏感词加入到HashMap中，构建DFA算法模型
 *
 * @author : GZL
 * @version 1.0
 * @date ： 2017-11-24
 */
@Slf4j
@Component
public class SensitiveWordInit {

    public static final String SENSITIVE_WORDS_FILE = "sensitiveWords.dat";

    public static final String SENSITIVE_WORDS_MAP = "sensitiveWordMap";

    public static final String SENSITIVE_FILE_PATH = "~/swift/sensitiveWord";

    @Autowired
    private RedisCacheService redisCacheService;

    private Lock lock = new ReentrantLock(true);

    /**
     * 初始化敏感词库
     */
    @PostConstruct
    public void initKeyWord() {
        try {
            //读取敏感词库
            Set<String> keyWordSet = readSensitiveWordFile();
            //将敏感词库保存入缓存中
            redisCacheService.set(SENSITIVE_WORDS_MAP, addSensitiveWordToHashMap(keyWordSet));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Map<String, Object> getSensitiveWordMap() {
        Object sensitiveWordMap = redisCacheService.get(SENSITIVE_WORDS_MAP);
        if (sensitiveWordMap != null) {
            return (Map<String, Object>) sensitiveWordMap;
        }
        try {
            lock.lock();
            Set<String> keyWordSet = readSensitiveWordFile();
            Map<String, Object> objectMap = addSensitiveWordToHashMap(keyWordSet);
            redisCacheService.set(SENSITIVE_WORDS_MAP, objectMap);
            return objectMap;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 读取敏感词库，将敏感词放入HashSet中，构建一个DFA算法模型
     *
     * @param keyWordSet 敏感词库
     */
    public Map<String, Object> addSensitiveWordToHashMap(Set<String> keyWordSet) {
        //初始化敏感词容器，减少扩容操作
        Map<String, Object> map = new HashMap(keyWordSet.size());
        String key;
        Map nowMap;
        Map<String, String> newWorMap;
        //迭代keyWordSet
        for (String aKeyWordSet : keyWordSet) {
            //关键字
            key = aKeyWordSet;
            nowMap = map;
            for (int i = 0; i < key.length(); i++) {
                //转换成char型
                char keyChar = key.charAt(i);
                //获取
                Object wordMap = nowMap.get(keyChar);
                //如果存在该key，直接赋值
                if (wordMap != null) {
                    nowMap = (Map) wordMap;
                } else {     //不存在则，则构建一个map，同时将isEnd设置为0，因为他不是最后一个
                    newWorMap = new HashMap<>(3);
                    //不是最后一个
                    newWorMap.put("isEnd", "0");
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }
                if (i == key.length() - 1) {
                    //最后一个
                    nowMap.put("isEnd", "1");
                }
            }
        }
        return map;
    }

    /**
     * 读取敏感词库中的内容，将内容添加到set集合中
     *
     * @return
     * @throws Exception
     */
    private Set<String> readSensitiveWordFile() {
        Set<String> resultSet = new HashSet<>();
        String filepath = SENSITIVE_FILE_PATH + File.separator + SENSITIVE_WORDS_FILE;
        File file = new File(filepath);
        // 如果不存在则新建词库
        if (!file.exists()) {
            File parentFile = file.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdir();
            }
            try {
                file.createNewFile();
            } catch (IOException e) {
                log.error("文件目录：{}，创建敏感词库失败", filepath, e);
            }

        }
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        try {
            inputStream = new FileInputStream(file);
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String txt;
            while ((txt = bufferedReader.readLine()) != null) {
                resultSet.add(txt);
            }
        } catch (Exception e) {
            log.error("文件目录：{}，读取敏感词库数据失败", filepath, e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                log.error("文件流关闭异常", e);
            }
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                log.error("文件流关闭异常", e);
            }

        }
        String sensitiveWords = StringUtils.join(resultSet, ",");
        //将敏感词库保存入缓存中
        redisCacheService.set("sensitiveWords", sensitiveWords);
        return resultSet;
    }


    /**
     * 更新敏感词库中的内容
     *
     * @return
     * @throws Exception
     */
    public Boolean updateSensitiveWordFile(String sensitiveWords) {
        Set<String> resultSet = new HashSet<>();
        String[] sensitiveWordArray = sensitiveWords.split("\n");
        String filepath = SENSITIVE_FILE_PATH + File.separator + SENSITIVE_WORDS_FILE;
        File file = new File(filepath);
        // 如果不存在则新建词库
        if (!file.exists()) {
            File parentFile = file.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdir();
            }
            try {
                file.createNewFile();
            } catch (IOException e) {
                log.error("文件目录：{}，创建敏感词库失败", filepath, e);
            }
        }
        try {
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            bufferedWriter.write(sensitiveWords);
            bufferedWriter.flush();
            bufferedWriter.close();
            Collections.addAll(resultSet, sensitiveWordArray);
            //重新生成敏感词库并更新缓存中的敏感词库
            redisCacheService.set(SENSITIVE_WORDS_MAP, addSensitiveWordToHashMap(resultSet));
            redisCacheService.set("sensitiveWords", StringUtils.join(resultSet, ","));
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("文件目录：{}，读取敏感词库数据失败", filepath, e);
        }
        return Boolean.FALSE;
    }
}
